﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web;
using System.Web.Caching;

namespace Cache
{
    /// <summary>
    /// Web 缓存
    /// </summary>
    /// <typeparam name="T"></typeparam>
	public class WebCache<T>
	{
		private string _name;
		private TimeSpan _slidingExpiration = System.Web.Caching.Cache.NoSlidingExpiration;
		private DateTime _absoluteExpiration = System.Web.Caching.Cache.NoAbsoluteExpiration;
		private TimeSpan _intervalExpiration;
		private bool _isIntervalBased = false;
		private Func<T> _valueFactory = null;
		private System.Threading.Timer _intervalTimer = null;

		/// <summary>
		/// Creates a new WebCache object with a GUID for a name.
		/// </summary>
		public WebCache()
		{
			_name = Guid.NewGuid().ToString();
		}

		/// <summary>
		/// Creates a new WebCache object with a GUID for a name as well as the specified intervalExpiration and valueFactory.
		/// </summary>
		/// <param name="intervalExpiration">The interval at which the object should be set by the valueFactory.</param>
		/// <param name="valueFactory">The value that this Func returns will be used to set the value in the cache.</param>
		public WebCache(TimeSpan intervalExpiration, Func<T> valueFactory)
		{
			_name = Guid.NewGuid().ToString();
			_valueFactory = valueFactory;
			_isIntervalBased = true;
			_intervalExpiration = intervalExpiration;
		}

		/// <summary>
		/// Creates a new WebCache object with a GUID for a name and the specified slidingExpiration.
		/// </summary>
		/// <param name="slidingExpiration">The time between when the cache was last accessed and when it expires.</param>
		public WebCache(TimeSpan slidingExpiration)
		{
			_name = Guid.NewGuid().ToString();
			_slidingExpiration = slidingExpiration;
		}

		/// <summary>
		/// Creates a new WebCache object with a GUID for a name and the specified absoluteExpiration.
		/// </summary>
		/// <param name="absoluteExpiration">The time at which the cache should expire.</param>
		public WebCache(DateTime absoluteExpiration)
		{
			_name = Guid.NewGuid().ToString();
			_absoluteExpiration = absoluteExpiration;
		}

		/// <summary>
		/// Creates a new WebCache object with the specified name;
		/// </summary>
		/// <param name="name">The name of the cache.  This must be unique.</param>
		public WebCache(string name)
		{
			_name = name;
		}

		/// <summary>
		/// Creates a new WebCache object with the specified name, intervalExpiration, and valueFactory.
		/// </summary>
		/// <param name="name">The name of the cache.  This must be unique.</param>
		/// <param name="intervalExpiration">The interval at which the object should be set by the valueFactory.</param>
		/// <param name="valueFactory">The value that this Func returns will be used to set the value in the cache.</param>
		public WebCache(string name, TimeSpan intervalExpiration, Func<T> valueFactory = null)
		{
			_name = name;
			_valueFactory = valueFactory;
			_isIntervalBased = true;
			_intervalExpiration = intervalExpiration;
		}

		/// <summary>
		/// Creates a new WebCache object with the specified name and slidingExpiration.
		/// </summary>
		/// <param name="name">The name of the cache.  This must be unique.</param>
		/// <param name="slidingExpiration">The time between when the cache was last accessed and when it expires.</param>
		public WebCache(string name, TimeSpan slidingExpiration)
		{
			_name = name;
			_slidingExpiration = slidingExpiration;
		}

		/// <summary>
		/// Creates a new WebCache object with the specified name and absoluteExpiration.
		/// </summary>
		/// <param name="name">The name of the cache.  This must be unique.</param>
		/// <param name="absoluteExpiration">The time at which the cache should expire.</param>
		public WebCache(string name, DateTime absoluteExpiration)
		{
			_name = name;
			_absoluteExpiration = absoluteExpiration;
		}

		private void SetNewValue(object stateInfo)
		{
			Value = _valueFactory();
		}

		/// <summary>
		/// Get or Set the value for the WebCache.  If an intervalExpiration and valueFactory were specified in the constructor, then we will call the valueFactory on the first Get and then repeatedly at the specified interval.
		/// </summary>
		public T Value
		{
			get
			{
				if (_isIntervalBased && _intervalTimer == null && _valueFactory != null)
				{
					T val = _valueFactory();
					Value = val;

					var tc = new System.Threading.TimerCallback(SetNewValue);
					_intervalTimer = new System.Threading.Timer(tc, null, _intervalExpiration, _intervalExpiration);

					return val;
				}

				object o = HttpRuntime.Cache[_name];
				if (o is T)
				{
					return (T)o;
				}
				else
				{
					return default(T);
				}
			}
			set
			{
				if (_isIntervalBased)
				{
                    HttpRuntime.Cache.Insert(_name, value, null, System.Web.Caching.Cache.NoAbsoluteExpiration, System.Web.Caching.Cache.NoSlidingExpiration, CacheItemPriority.NotRemovable, null);
				}
				else
				{
					HttpRuntime.Cache.Insert(_name, value, null, _absoluteExpiration, _slidingExpiration, CacheItemPriority.Default, null);
				}
			}
		}		

	}
}
